﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Windows;
using System.Windows.Media;
using System.Text.RegularExpressions;
using System.Globalization;
using SharpVectors.Renderer.Xaml;
using SharpVectors.Dom.Svg;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Threading;
using System.Reflection;

namespace XamlTune
{
    public class XamlTuner
    {

        private class convertWorker
        {
            public convertWorker(bool isSilverlight
                            , bool replaceRectangleGeometry
                            , bool moveCanvasAsBoundingBox
                            , bool removeNullValues
                            , bool removeDefaultValues
                            , bool excludeCollapsedFromBoundingBox
                            , string xamlInput)
            {
                this.isSilverlight = isSilverlight;
                this.replaceRectangleGeometry = replaceRectangleGeometry;
                this.moveCanvasAsBoundingBox = moveCanvasAsBoundingBox;
                this.removeNullValues = removeNullValues;
                this.removeDefaultValues = removeDefaultValues;
                this.excludeCollapsedFromBoundingBox = excludeCollapsedFromBoundingBox;
                this.xamlInput = xamlInput;
            }

            private bool isSilverlight;
            private bool replaceRectangleGeometry;
            private bool moveCanvasAsBoundingBox;
            private bool removeNullValues;
            private bool removeDefaultValues;
            private bool excludeCollapsedFromBoundingBox;
            private string xamlInput;
            
            private string output;

            private Exception error;

            private void tune()
            {
                try
                {
                    XamlTuner tuner = new XamlTuner(isSilverlight, replaceRectangleGeometry, moveCanvasAsBoundingBox, removeNullValues
                         , removeDefaultValues, excludeCollapsedFromBoundingBox, xamlInput);

                    output = tuner.convert();
                }
                catch (Exception ex)
                {
                    error = ex;
                }
            }

            public string Tune()
            {
                Thread th = new Thread(new ThreadStart(tune));
                th.SetApartmentState(ApartmentState.STA);
                th.Start();
                th.Join();
                if (error != null)
                    throw new TargetInvocationException(error);
                return output;
            }
        }

        private XamlTuner(bool isSilverlight
            , bool replaceRectangleGeometry
            , bool moveCanvasAsBoundingBox
            , bool removeNullValues
            , bool removeDefaultValues
            , bool excludeCollapsedFromBoundingBox
            , string xamlInput)
        {
            mgr = new XmlNamespaceManager(new NameTable());
            mgr.AddNamespace("wpf", wpfNamespace);
            mgr.AddNamespace("x", xNamespace);
            mgr.AddNamespace("sl", slNamespace);
            this.isSilverlight = isSilverlight;
            this.replaceRectangleGeometry = replaceRectangleGeometry;
            this.moveCanvasAsBoundingBox = moveCanvasAsBoundingBox;
            this.removeNullValues = removeNullValues;
            this.removeDefaultValues = removeDefaultValues;
            this.xamlInput = xamlInput;
            this.excludeCollapsedFromBoundingBox = excludeCollapsedFromBoundingBox;
        }

        public static string Tune(bool isSilverlight
            , bool replaceRectangleGeometry
            , bool moveCanvasAsBoundingBox
            , bool removeNullValues
            , bool removeDefaultValues
            , bool excludeCollapsedFromBoundingBox
            , string xamlInput)
        {
            convertWorker worker = new convertWorker(isSilverlight
                                , replaceRectangleGeometry
                                , moveCanvasAsBoundingBox
                                , removeNullValues
                                , removeDefaultValues
                                , excludeCollapsedFromBoundingBox
                                , xamlInput);
            return worker.Tune();
        }


        public const string slNamespace = "http://schemas.microsoft.com/client/2007";
        public const string wpfNamespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
        public const string xNamespace = "http://schemas.microsoft.com/winfx/2006/xaml";

        private string currentNamespace = "sl";
        XmlNamespaceManager mgr;
        XmlDocument doc;
        bool isSilverlight;
        bool replaceRectangleGeometry;
        bool moveCanvasAsBoundingBox;
        bool removeNullValues;
        bool removeDefaultValues;
        bool excludeCollapsedFromBoundingBox;
        string xamlInput;

        private string convert()
        {
            StringBuilder sb = new StringBuilder(xamlInput);

            if (isSilverlight == true)
                sb.Replace(wpfNamespace, slNamespace);
            else
                sb.Replace(slNamespace, wpfNamespace);

            doc = new XmlDocument(mgr.NameTable);
            try
            {
                doc.LoadXml(sb.ToString());
            }
            catch
            {
                return null;
            }

            if (isSilverlight == true)
                currentNamespace = "sl";
            else
                currentNamespace = "wpf";

            if (replaceRectangleGeometry == true)
            {
                replaceRectangleGeomtry(doc, currentNamespace);
            }

            replaceRenderTransformTemplate(doc);

            replaceAllPathDataSilverlight(doc);



            if (moveCanvasAsBoundingBox == true)
            {
                boundCanvas(doc.SelectSingleNode("/"));
            }

            if (removeNullValues == true)
            {
                removeDefaultAttribute(doc, "*", nullString);
            }

            if (removeDefaultValues == true)
            {
                removeDefaultAttribute(doc, "Opacity", "1");
                removeDefaultAttribute(doc, "StrokeStartLineCap", Path.StrokeStartLineCapProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "StrokeEndLineCap", Path.StrokeEndLineCapProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "StrokeDashCap", Path.StrokeDashCapProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "StrokeLineJoin", Path.StrokeLineJoinProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "StrokeMiterLimit", Path.StrokeMiterLimitProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "StrokeDashOffset", Path.StrokeDashOffsetProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "StrokeDashArray", Path.StrokeDashArrayProperty.DefaultMetadata.DefaultValue.ToString());
                removeDefaultAttribute(doc, "Canvas.Left", "0");
                removeDefaultAttribute(doc, "Canvas.Top", "0");
                if (isSilverlight == true)
                    removeDefaultAttribute(doc, "ClipToBounds");
            }

            sb = new StringBuilder(doc.OuterXml);
            if (isSilverlight == true)
                sb.Replace(wpfNamespace, slNamespace);
            else
                sb.Replace(slNamespace, wpfNamespace);


            XmlReader r = new XmlTextReader(new System.IO.StringReader(sb.ToString()));
            System.IO.StringWriter sw = new System.IO.StringWriter();
            XmlTextWriter w = new XmlTextWriter(sw);
            w.Formatting = Formatting.Indented;
            while (r.Read())
                w.WriteNode(r, false);
            w.Close();
            r.Close();
            return sw.ToString();
        }

        //public static string Tune(bool isSilverlight
        //    , bool replaceRectangleGeometry
        //    , bool moveCanvasAsBoundingBox
        //    , bool removeNullValues
        //    , bool removeDefaultValues
        //    , bool excludeCollapsedFromBoundingBox
        //    , string xamlInput)
        //{
        //    XamlTune tuner = new XamlTune(isSilverlight, replaceRectangleGeometry, moveCanvasAsBoundingBox, removeNullValues
        //        , removeDefaultValues, excludeCollapsedFromBoundingBox, xamlInput);
        //    return tuner.convert();
        //}

        private void replaceRenderTransformTemplate(XmlDocument doc)
        {
            XmlNodeList xnl = doc.SelectNodes("//@RenderTransform", mgr);
            foreach (XmlAttribute rt in xnl)
            {
                XmlElement elem = rt.OwnerElement;
                double left = 0;
                double top = 0;

                getPositionFromAttribute(elem, out left, out top);

                string[] vals = rt.Value.Split(',');
                left += double.Parse(vals[4], slCulture);
                top += double.Parse(vals[5], slCulture);
                elem.SetAttribute("Canvas.Left", Math.Round(left, roundCount).ToString(slCulture));
                elem.SetAttribute("Canvas.Top", Math.Round(top, roundCount).ToString(slCulture));
                if (rt.Value.StartsWith("1,0,0,1"))
                {
                    elem.RemoveAttribute("RenderTransform");
                }
                else
                {
                    vals[4] = "0";
                    vals[5] = "0";
                    rt.Value = string.Join(",", vals);
                }
            }
        }


        private void replaceRectangleGeomtry(XmlDocument doc, string currentNamespace)
        {
            //Select Path with only one RectangleGeometry
            foreach (XmlElement elem in doc.SelectNodes("//" + currentNamespace + ":Path[count(" + currentNamespace + ":Path.Data/" + currentNamespace + ":RectangleGeometry) = 1]", mgr))
            {
                XmlElement rectGeom = (XmlElement)elem.SelectSingleNode(currentNamespace + ":Path.Data/" + currentNamespace + ":RectangleGeometry", mgr);
                XmlElement rec = doc.CreateElement("Rectangle", isSilverlight == true ? slNamespace : wpfNamespace);
                string radiusx = rectGeom.GetAttribute("RadiusX");
                if (!string.IsNullOrEmpty(radiusx))
                    rec.SetAttribute("RadiusX", radiusx);

                string radiusy = rectGeom.GetAttribute("RadiusY");
                if (!string.IsNullOrEmpty(radiusy))
                    rec.SetAttribute("RadiusY", radiusy);

                foreach (XmlElement child in elem.ChildNodes)
                {
                    if (StringComparer.Ordinal.Compare(child.LocalName, "Path.Data") != 0)
                    {
                        XmlElement recChild = doc.CreateElement(child.LocalName.Replace("Path.", "Rectangle."), isSilverlight == true ? slNamespace : wpfNamespace);
                        rec.AppendChild(recChild);
                        recChild.InnerXml = child.InnerXml;
                    }
                }

                string srect = rectGeom.GetAttribute("Rect");
                string[] coords = srect.Split(',');

                double x = 0;
                string left = elem.GetAttribute("Canvas.Left");
                if (!string.IsNullOrEmpty(left))
                {
                    double left1 = double.Parse(left, slCulture);
                    double left2 = double.Parse(coords[0], slCulture);
                    x = left1 + left2;
                    rec.SetAttribute("Canvas.Left", Math.Round(x, roundCount).ToString(slCulture));
                }
                else
                {
                    rec.SetAttribute("Canvas.Left", coords[0]);
                    x = double.Parse(coords[0], slCulture);
                }

                double y = 0;
                string top = elem.GetAttribute("Canvas.Top");
                if (!string.IsNullOrEmpty(top))
                {
                    double top1 = double.Parse(top, slCulture);
                    double top2 = double.Parse(coords[1], slCulture);
                    y = top1 + top2;
                    //if (Double.IsInfinity(y))
                    //    Debug.Fail("");
                    rec.SetAttribute("Canvas.Top", Math.Round(y, roundCount).ToString(slCulture));
                }
                else
                {
                    rec.SetAttribute("Canvas.Top", coords[1]);
                    y = double.Parse(coords[1], slCulture);
                }

                rec.SetAttribute("Width", coords[2]);
                rec.SetAttribute("Height", coords[3]);

                foreach (XmlAttribute attr in elem.Attributes)
                {
                    rec.SetAttribute(attr.LocalName, attr.NamespaceURI, attr.Value);
                }
                elem.ParentNode.InsertAfter(rec, elem);
                elem.ParentNode.RemoveChild(elem);

                foreach (XmlElement grad in rec.SelectNodes(".//*[@MappingMode='Absolute']"))
                {
                    string smatrix = grad.GetAttribute("Transform");
                    Matrix matrix = Matrix.Identity;
                    if (!string.IsNullOrEmpty(smatrix))
                    {
                        matrix = Matrix.Parse(smatrix);
                        matrix.Translate(-x, -y);
                        grad.SetAttribute("Transform", matrix.ToString(slCulture));
                    }
                    else
                    {
                        string str = grad.GetAttribute("GradientOrigin");
                        if (!string.IsNullOrEmpty(str))
                        {
                            str = offsetPoints(str, new Point(x, y));
                            grad.SetAttribute("GradientOrigin", str);
                        }
                        str = grad.GetAttribute("StartPoint");
                        if (!string.IsNullOrEmpty(str))
                        {
                            str = offsetPoints(str, new Point(x, y));
                            grad.SetAttribute("StartPoint", str);
                        }
                        str = grad.GetAttribute("EndPoint");
                        if (!string.IsNullOrEmpty(str))
                        {
                            str = offsetPoints(str, new Point(x, y));
                            grad.SetAttribute("EndPoint", str);
                        }
                        str = grad.GetAttribute("Center");
                        if (!string.IsNullOrEmpty(str))
                        {
                            str = offsetPoints(str, new Point(x, y));
                            grad.SetAttribute("Center", str);
                        }
                    }
                }
            }
            //<Path Stroke="#FFFF8B00" StrokeThickness="2" StrokeMiterLimit="4" x:Name="rect36597">
            //  <Path.Data>
            //    <RectangleGeometry RadiusX="5" RadiusY="5" Rect="10.99647,78.996483,328.00708,288.00705" />
            //  </Path.Data>
            //</Path>
        }

        private void boundCanvas(XmlNode startElement)
        {
            XmlNodeList xnl = startElement.SelectNodes(currentNamespace + ":Canvas", mgr);
            foreach (XmlElement elemCanvas in xnl)
            {
                boundCanvas(elemCanvas);
            }

            if (startElement.LocalName == "Canvas")
            {
                XmlElement elem = (XmlElement)startElement;
                //if (string.IsNullOrEmpty(elem.GetAttribute("Width")))
                //    return;

                Rect? bound = null;

                foreach (XmlElement child in startElement.ChildNodes)
                {
                    if (excludeCollapsedFromBoundingBox && child.GetAttribute("Visibility") == "Collapsed")
                        continue;

                    if (bound == null)
                        bound = getBound(child, bound);
                    else
                    {
                        Rect? newBound = getBound(child, bound);
                        if (newBound != null)
                            bound = Rect.Union(bound.Value, newBound.Value);
                    }
                }

                if (bound.HasValue)
                {
                    string renderTransform = elem.GetAttribute("RenderTransform");
                    Matrix mtrans = Matrix.Identity;
                    Point localPosition = bound.Value.Location;
                    if (!string.IsNullOrEmpty(renderTransform))
                    {
                        mtrans = Matrix.Parse(renderTransform);
                        mtrans.OffsetX = 0;
                        mtrans.OffsetY = 0;

                        //mtrans.Invert();
                        localPosition = mtrans.Transform(localPosition);
                    }

                    double left;
                    double top;
                    getPositionFromAttribute(elem, out left, out top);

                    localPosition.X += left;
                    localPosition.Y += top;

                    //replacePoints(elem.SelectNodes("@RenderTransform", mgr), localPosition, 2, 1);
                    elem.SetAttribute("Canvas.Left", Math.Round(localPosition.X, roundCount).ToString(slCulture));
                    //if (Double.IsInfinity(localPosition.Y))
                    //    Debug.Fail("");
                    elem.SetAttribute("Canvas.Top", Math.Round(localPosition.Y, roundCount).ToString(slCulture));

                    //Width and Height values are not affected by rendertransform matrix
                    elem.SetAttribute("Width", Math.Round(bound.Value.Width, roundCount).ToString(slCulture));
                    elem.SetAttribute("Height", Math.Round(bound.Value.Height, roundCount).ToString(slCulture));

                    foreach (XmlElement child in startElement.ChildNodes)
                    {
                        offsetElement(child, bound.Value.Location);
                    }
                }
            }
        }

        private void offsetElement(XmlElement child, Point location)
        {
            //replacePoints(child.SelectNodes("@RenderTransform", mgr), location, 2, 1);

            double left;
            double top;
            getPositionFromAttribute(child, out left, out top);

            left -= location.X;
            top -= location.Y;
            //if (Double.IsInfinity(left))
            //    Debug.Fail("");
            child.SetAttribute("Canvas.Left", Math.Round(left, roundCount).ToString(slCulture));
            child.SetAttribute("Canvas.Top", Math.Round(top, roundCount).ToString(slCulture));
        }

        private static void getPositionFromAttribute(XmlElement child, out double left, out double top)
        {
            string sleft = child.GetAttribute("Canvas.Left");
            left = 0;
            if (!string.IsNullOrEmpty(sleft))
                left = double.Parse(sleft, slCulture.NumberFormat);

            string stop = child.GetAttribute("Canvas.Top");
            top = 0;
            if (!string.IsNullOrEmpty(stop))
                top = double.Parse(stop, slCulture.NumberFormat);
        }

        private Rect? getBound(XmlElement child, Rect? defaultBounds)
        {
            //string width = child.GetAttribute("Width");

            double left;
            double top;
            getPositionFromAttribute(child, out left, out top);

            string swidth = child.GetAttribute("Width");
            double width = 0;
            if (!string.IsNullOrEmpty(swidth))
                width = double.Parse(swidth, slCulture.NumberFormat);

            string sheight = child.GetAttribute("Height");
            double height = 0;
            if (!string.IsNullOrEmpty(sheight))
                height = double.Parse(sheight, slCulture.NumberFormat);

            string renderTransform = child.GetAttribute("RenderTransform");
            Matrix mtrans = Matrix.Identity;
            if (!string.IsNullOrEmpty(renderTransform))
            {
                mtrans = Matrix.Parse(renderTransform);
                left += mtrans.OffsetX;
                top += mtrans.OffsetY;
                mtrans.OffsetX = 0;
                mtrans.OffsetY = 0;
            }

            Rect bounds = new Rect();
            if (child.LocalName == "Path")
            {
                Path path = GetPath(child.OuterXml);
                Rect pathBounds = path.Data.Bounds;
                bounds = new Rect(left, top, pathBounds.Width, pathBounds.Height);
            }
            else if (!string.IsNullOrEmpty(swidth))
            {
                bounds = new Rect(left, top, width, height);
            }
            else if (defaultBounds.HasValue)
            {
                bounds = defaultBounds.Value;
            }
            else
                return null;

            Point pt1 = bounds.Location;
            pt1.Offset(mtrans.OffsetX, mtrans.OffsetY);

            Point pt2 = mtrans.Transform(new Point(bounds.Width, bounds.Height));
            return new Rect(pt1.X, pt1.Y, pt2.X, pt2.Y);
        }

        private void replaceAllPathDataSilverlight(XmlDocument doc)
        {
            XmlNodeList xnl = doc.SelectNodes("//" + currentNamespace + ":Path", mgr);
            foreach (XmlElement pathElem in xnl)
            {
                replacePathDataSilverlight(pathElem);
            }
        }


        private string offsetPointsData(string data, Point location)
        {
            PathGeometry geom = new PathGeometry();
            PathFigureCollection figCol = PathFigureCollection.Parse(data);
            foreach (var item in figCol)
            {
                offsetPointsFigure(item, location);
            }

            return figCol.ToString(slCulture);
        }

        private void offsetPointsFigure(PathFigure item, Point location)
        {
            Point pt = item.StartPoint;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);
            item.StartPoint = pt;

            foreach (var seg in item.Segments)
            {
                if (seg is ArcSegment)
                    offsetPointsSegment((ArcSegment)seg, location);
                else if (seg is BezierSegment)
                    offsetPointsSegment((BezierSegment)seg, location);
                else if (seg is LineSegment)
                    offsetPointsSegment((LineSegment)seg, location);
                else if (seg is PolyBezierSegment)
                    offsetPointsSegment((PolyBezierSegment)seg, location);
                else if (seg is PolyLineSegment)
                    offsetPointsSegment((PolyLineSegment)seg, location);
                else if (seg is PolyQuadraticBezierSegment)
                    offsetPointsSegment((PolyQuadraticBezierSegment)seg, location);
                else if (seg is QuadraticBezierSegment)
                    offsetPointsSegment((QuadraticBezierSegment)seg, location);
            }
        }

        private void offsetPointsSegment(QuadraticBezierSegment quadraticBezierSegment, Point location)
        {
            Point pt = quadraticBezierSegment.Point1;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);
            quadraticBezierSegment.Point1 = pt;

            pt = quadraticBezierSegment.Point2;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);
            quadraticBezierSegment.Point2 = pt;
        }

        private void offsetPointsSegment(PolyQuadraticBezierSegment polyQuadraticBezierSegment, Point location)
        {
            PointCollection pcol = new PointCollection();
            foreach (var oldpt in polyQuadraticBezierSegment.Points)
            {
                Point pt = oldpt;
                pt.Offset(-location.X, -location.Y);
                pt.X = Math.Round(pt.X, roundCount);
                pt.Y = Math.Round(pt.Y, roundCount);
                pcol.Add(pt);
            }
            polyQuadraticBezierSegment.Points = pcol;
        }

        private void offsetPointsSegment(LineSegment lineSegment, Point location)
        {
            Point pt = lineSegment.Point;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);
            lineSegment.Point = pt;
        }

        private void offsetPointsSegment(BezierSegment bezierSegment, Point location)
        {
            Point pt = bezierSegment.Point1;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);

            bezierSegment.Point1 = pt;

            pt = bezierSegment.Point2;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);

            bezierSegment.Point2 = pt;

            pt = bezierSegment.Point3;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);

            bezierSegment.Point3 = pt;
        }

        private void offsetPointsSegment(PolyLineSegment polyLineSegment, Point location)
        {
            PointCollection pcol = new PointCollection();
            foreach (var oldpt in polyLineSegment.Points)
            {
                Point pt = oldpt;
                pt.Offset(-location.X, -location.Y);
                pt.X = Math.Round(pt.X, roundCount);
                pt.Y = Math.Round(pt.Y, roundCount);

                pcol.Add(pt);
            }
            polyLineSegment.Points = pcol;
        }

        private void offsetPointsSegment(PolyBezierSegment polyBezierSegment, Point location)
        {
            PointCollection pcol = new PointCollection();
            foreach (var oldpt in polyBezierSegment.Points)
            {
                Point pt = oldpt;
                pt.Offset(-location.X, -location.Y);
                pt.X = Math.Round(pt.X, roundCount);
                pt.Y = Math.Round(pt.Y, roundCount);

                pcol.Add(pt);
            }
            polyBezierSegment.Points = pcol;
        }

        private void offsetPointsSegment(ArcSegment arcSegment, Point location)
        {
            Point pt = arcSegment.Point;
            pt.Offset(-location.X, -location.Y);
            pt.X = Math.Round(pt.X, roundCount);
            pt.Y = Math.Round(pt.Y, roundCount);

            arcSegment.Point = pt;
        }

        private void replacePathDataSilverlight(XmlElement pathElem)
        {
            XmlNode elem = pathElem.SelectSingleNode(currentNamespace + ":Path.Data", mgr);
            bool elemIsAttribute = false;
            if (elem == null)
            {
                elem = pathElem.SelectSingleNode("@Data", mgr);
                elemIsAttribute = true;
            }

            if (elem == null)
                return;



            Rect bounds = Rect.Empty;
            string innerText = elem.InnerText;
            if (!string.IsNullOrEmpty(innerText))
            {
                if (moveCanvasAsBoundingBox == true)
                {
                    PathFigureCollection pfc = PathFigureCollection.Parse(innerText);
                    PathGeometry geom = new PathGeometry();
                    geom.Figures = pfc;
                    bounds = geom.Bounds;

                    string data = offsetPointsData(innerText, bounds.Location);

                    if (elemIsAttribute)
                        pathElem.RemoveAttributeNode((XmlAttribute)elem);
                    else
                        pathElem.RemoveChild(elem);

                    double left;
                    double top;
                    getPositionFromAttribute(pathElem, out left, out top);

                    pathElem.SetAttribute("Data", data);
                    pathElem.SetAttribute("Canvas.Left", Math.Round(bounds.Left + left, roundCount).ToString(slCulture));
                    //if (Double.IsInfinity(bounds.Top))
                    //    Debug.Fail("");
                    pathElem.SetAttribute("Canvas.Top", Math.Round(bounds.Top + top, roundCount).ToString(slCulture));
                }
            }
            else if (!elemIsAttribute)
            {
                XmlElement firstChild = elem.FirstChild as XmlElement;
                if (firstChild != null)
                {
                    if (firstChild.LocalName == "PathGeometry")
                    {

                        XmlAttribute attrib = firstChild.SelectSingleNode("@Figures", mgr) as XmlAttribute;
                        if (attrib != null)
                        {
                            string data = attrib.InnerText;
                            PathFigureCollection pfc = PathFigureCollection.Parse(data);
                            PathGeometry geom = new PathGeometry();
                            geom.Figures = pfc;

                            if (moveCanvasAsBoundingBox == true)
                            {
                                bounds = geom.Bounds;
                                if (!bounds.IsEmpty)
                                {
                                    double left;
                                    double top;
                                    getPositionFromAttribute(pathElem, out left, out top);

                                    pathElem.SetAttribute("Canvas.Left", Math.Round(left + bounds.Left, roundCount).ToString(slCulture));
                                    //if (Double.IsInfinity(bounds.Top))
                                    //    Debug.Fail("");
                                    pathElem.SetAttribute("Canvas.Top", Math.Round(top + bounds.Top, roundCount).ToString(slCulture));

                                    data = offsetPointsData(data, bounds.Location);
                                }
                                else
                                {
                                    //No path data... Remove Path element
                                    pathElem.ParentNode.RemoveChild(pathElem);
                                    return;
                                }
                            }

                            if (pfc.Count == 1)
                            {
                                //One figure move points in Data property                                
                                pathElem.RemoveChild(elem);
                                pathElem.SetAttribute("Data", data);

                            }
                            else
                            {
                                XmlElement pgf = elem.OwnerDocument.CreateElement("PathGeometry.Figures", slNamespace);
                                firstChild.AppendChild(pgf);

                                StringBuilder sb = new StringBuilder();
                                pfc = PathFigureCollection.Parse(data);
                                foreach (var item in pfc)
                                {
                                    sb.Append(XamlWriter.Save(item));
                                }
                                if (isSilverlight == true)
                                    sb.Replace("xmlns=\"" + wpfNamespace + "\"", "");
                                pgf.InnerXml = sb.ToString();
                                firstChild.RemoveAttributeNode(attrib);

                                if (pfc.Count == 0)
                                {
                                    //No path data... Remove Path element
                                    pathElem.ParentNode.RemoveChild(pathElem);
                                    return;
                                }

                            }

                            //replacePointsForOneAttribute(m, location, attrib);
                            //elem.ParentNode.RemoveChild(elem);
                            //return attrib.InnerText;
                        }
                        //else
                        //{
                        //    XmlElement pgf = firstChild.SelectSingleNode("sl:PathGeometry.Figures", mgr);


                        //}

                    }
                }
                else if (moveCanvasAsBoundingBox == true)
                {
                    System.Windows.Shapes.Path path = GetPath(pathElem.OuterXml);
                    bounds = path.Data.Bounds;

                    double left;
                    double top;
                    getPositionFromAttribute(pathElem, out left, out top);

                    pathElem.SetAttribute("Canvas.Left", Math.Round(left + bounds.Left, roundCount).ToString(slCulture));
                    //if (Double.IsInfinity(bounds.Top))
                    //    Debug.Fail("");
                    pathElem.SetAttribute("Canvas.Top", Math.Round(top + bounds.Top, roundCount).ToString(slCulture));
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":PathFigure/@StartPoint", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":LineSegment/@Point", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":ArcSegment/@Point", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":BezierSegment/@Point1", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":BezierSegment/@Point2", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":BezierSegment/@Point3", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":PolyBezierSegment/@Points", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":PolyQuadraticBezierSegment/@Points", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":QuadraticBezierSegment/@Points", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":EllipseGeometry/@Center", mgr), bounds.Location);
                    replacePoints(firstChild.SelectNodes(".//" + currentNamespace + ":LineGeometry/@StartPoint", mgr), bounds.Location);
                    replaceRect(firstChild.SelectNodes(".//" + currentNamespace + ":RectangleGeometry/@Rect", mgr), bounds.Location);

                }
            }

            if (moveCanvasAsBoundingBox == true)
            {
                replacePoints(pathElem.SelectNodes(currentNamespace + ":Path.Fill/*[@MappingMode='Absolute']/@Transform", mgr), bounds.Location, 2, 1);
                replacePoints(pathElem.SelectNodes(currentNamespace + ":Path.Fill/*[@MappingMode='Absolute']/@StartPoint", mgr), bounds.Location);
                replacePoints(pathElem.SelectNodes(currentNamespace + ":Path.Fill/*[@MappingMode='Absolute']/@EndPoint", mgr), bounds.Location);
            }
        }

        private static System.Windows.Shapes.Path GetPath(string xml)
        {
            StringBuilder sb = new StringBuilder("<Canvas  xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' >");
            sb.Append(xml);
            sb.Replace(slNamespace, wpfNamespace);
            sb.Append("</Canvas>");
            System.IO.MemoryStream ms = new System.IO.MemoryStream(Encoding.Default.GetBytes(sb.ToString()));

            Canvas cv = (Canvas)XamlReader.Load(ms);
            System.Windows.Shapes.Path path = (System.Windows.Shapes.Path)cv.Children[0];
            return path;
        }


        private void replaceRect(XmlNodeList xmlNodeList, Point location)
        {
            foreach (XmlNode node in xmlNodeList)
            {
                string pts = node.InnerText;
                pts = offsetPoints(pts, location);
                node.InnerText = pts;
            }
        }

        private bool replacePoints(XmlNodeList xmlNodeList, Point location)
        {
            bool result = false;
            foreach (XmlNode node in xmlNodeList)
            {
                string pts = node.InnerText;
                pts = offsetPoints(pts, location, null, 1);
                node.InnerText = pts;
                result = true;
            }
            return result;
        }

        private bool replacePoints(XmlNodeList xmlNodeList, Point location, int? start, int? count)
        {
            bool result = false;
            foreach (XmlNode node in xmlNodeList)
            {
                string pts = node.InnerText;
                pts = offsetPoints(pts, location, start, count);
                node.InnerText = pts;
                result = true;
            }
            return result;
        }

        private const int roundCount = 4;
        private static readonly Regex regexPoint = new Regex(@"\-?\d+(\.\d+)?,\-?\d+(\.\d+)?", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Multiline);
        private static readonly CultureInfo slCulture = CultureInfo.GetCultureInfo("en-us");
        private string offsetPoints(string points, Point location)
        {
            return offsetPoints(points, location, null, null);
        }

        private string offsetPoints(string points, Point location, int? start, int? count)
        {
            StringBuilder sbPt = new StringBuilder(points);

            int i = 0;
            int cnt = 0;
            List<KeyValuePair<Match, Point>> pts = new List<KeyValuePair<Match, Point>>();
            foreach (Match match in regexPoint.Matches(points))
            {
                if (start.HasValue && i < start)
                {
                    i++;
                    continue;
                }

                Point pt = Point.Parse(match.Value);
                pt.Offset(-location.X, -location.Y);
                pts.Add(new KeyValuePair<Match, Point>(match, pt));
                if (count.HasValue && cnt == count - 1)
                    break;
                cnt++;
                i++;
            }
            pts.Reverse();
            foreach (var item in pts)
            {
                sbPt.Replace(item.Key.Value, string.Concat(Math.Round(item.Value.X, roundCount).ToString(slCulture), ',', Math.Round(item.Value.Y, roundCount).ToString(slCulture)), item.Key.Index, item.Key.Length);
            }
            return sbPt.ToString();
        }

        private static void removeDefaultAttribute(XmlDocument doc, string attribute, string defaultValue)
        {
            foreach (XmlElement item in doc.SelectNodes(".//*[@" + attribute + "='" + defaultValue + "']"))
            {
                item.RemoveAttribute(attribute);
            }
        }

        private static void removeDefaultAttribute(XmlDocument doc, string attribute)
        {
            foreach (XmlElement item in doc.SelectNodes(".//*[@" + attribute + "]"))
            {
                item.RemoveAttribute(attribute);
            }
        }

        private const string nullString = "{x:Null}";
        private const string canvasNode = "Canvas";
        private const string pathNode = "Path";
    }
}
